package com.my.chapter18;

import net.mindView.innerclasses.Controller;
import net.mindView.innerclasses.Event;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.*;


public class practice11 {
    public static void main(String[] args) {
        GreenhouseControls2 gc =
                new GreenhouseControls2(".\\src\\com\\my\\chapter18\\GreenhouseConfig.txt");
        try {
            if(args.length == 1)
                gc.addEvent(new GreenhouseControls.Terminate(
                        Long.parseLong(args[0])));
        } catch(NumberFormatException e) {
            System.err.println("Terminate event is not added!");
            e.printStackTrace();
        }
        gc.run();
    }
}

class GreenhouseControls extends Controller {
    private boolean light = false;
    public class LightOn extends Event {

        public LightOn(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            light = true;
        }

        @Override
        public String toString() {
            return "Light is on";
        }
    }
    public class LightOff extends Event{

        public LightOff(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            light = false;
        }

        @Override
        public String toString() {
            return "Light is off";
        }
    }

    private boolean water = false;
    public class WaterOn extends Event{

        public WaterOn(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            water = true;
        }

        @Override
        public String toString() {
            return "Greenhouse water is on";
        }
    }
    public class WaterOff extends Event{

        public WaterOff(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            water = false;
        }

        @Override
        public String toString() {
            return "Greenhouse water is off";
        }
    }

    private String thermostat = "Day";
    public class ThermostatNight extends Event{

        public ThermostatNight(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            thermostat = "Night";
        }

        @Override
        public String toString() {
            return "Thermostat on night setting";
        }
    }
    public class ThermostatDay extends Event{

        public ThermostatDay(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            thermostat = "Day";
        }

        @Override
        public String toString() {
            return "Thermostat on day setting";
        }
    }

    private boolean fan = false;
    public class FanOn extends Event{

        public FanOn(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            fan = true;
        }

        @Override
        public String toString() {
            return "Fan is on";
        }
    }
    public class FanOff extends Event{

        public FanOff(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            fan = false;
        }

        @Override
        public String toString() {
            return "Fan is off";
        }
    }

    public class Bell extends Event{

        public Bell(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            addEvent(new GreenhouseControls.Bell(delayTime));
        }

        @Override
        public String toString() {
            return "Bing!";
        }
    }

    public class Restart extends Event{
        private Event[] eventList;

        public Restart(long delayTime,Event[] eventList) {
            super(delayTime);
            this.eventList = eventList;
            for(Event e:eventList){
                addEvent(e);
            }
        }

        @Override
        public void action() {
            for(Event e:eventList){
                e.start();
                addEvent(e);
            }
            start();
            addEvent(this);
        }

        @Override
        public String toString() {
            return "Restarting system";
        }
    }

    public static class Terminate extends Event{

        public Terminate(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            System.exit(0);
        }

        @Override
        public String toString() {
            return "Terminating";
        }
    }

}

class GreenhouseControls2 extends GreenhouseControls {
    class Restart extends Event {
        private Event[] eventList;
        public Restart(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            for(Event e : eventList) {
                e.start(); // Rerun each event
                addEvent(e);
            }
            start();
            addEvent(this); // Rerun this Event
        }
        public String toString() {
            return "Restarting system";
        }
        public void setEventList(Event[] eventList) {
            this.eventList = eventList;
        }
    }

    class GHEventFactory {
        LinkedList<EventCreator> events =
                new LinkedList<EventCreator>();
        class EventCreator {
            Constructor<Event> ctor;
            long offset;
            public EventCreator(Constructor<Event> ctor,
                                long offset) {
                this.ctor = ctor;
                this.offset = offset;
            }
        }

        @SuppressWarnings("unchecked")
        public GHEventFactory(String eventFile) {
            try {
                BufferedReader in =
                        new BufferedReader(
                                new FileReader(eventFile));
                String s;
                while((s = in.readLine())!= null) {
                    int colon = s.indexOf(':');
                    // Must use '$' instead of '.' to
                    // describe inner classes:
                    String className = s.substring(0, colon).trim();
                    Class<?> outer = className.equals("Restart") ?
                            GreenhouseControls2.class :
                            GreenhouseControls.class;
                    String type = "com.my.chapter18." +
                            outer.getSimpleName() + "$" + className;
                    long offset = Long.parseLong(
                            s.substring(colon + 1).trim());
                    Class<Event> eventClass =
                            (Class<Event>)Class.forName(type);

                    Constructor<Event> ctor =
                            eventClass.getConstructor(
                                    new Class<?>[] { outer, long.class });
                    events.add(new EventCreator(ctor, offset));
                }
            }catch(Exception e) {
                throw new RuntimeException(e);
            }
        }

        Iterator<Event> iterator() {
            return new Iterator<Event>() {
                Iterator<EventCreator> it = events.iterator();

                @Override
                public boolean hasNext() {
                    return  it.hasNext();
                }

                @Override
                public Event next() {
                    EventCreator ec = it.next();
                    Event returnVal = null;
                    try {
                        returnVal = ec.ctor.newInstance(
                                new Object[] {
                                        GreenhouseControls2.this, ec.offset
                                });
                    } catch(Exception e) {
                        throw new RuntimeException(e);
                    }
                    return returnVal;
                }
            };
        }
    }

    GHEventFactory gheFactory;
    public GreenhouseControls2(String initFile) {
        gheFactory = new GHEventFactory(initFile);
        LinkedList<Event> restartableEvents =
                new LinkedList<Event>();
        Iterator<Event> it = gheFactory.iterator();
        while(it.hasNext()) {
            Event e = it.next();
            if(e instanceof Bell || e instanceof Restart)
                continue;
            restartableEvents.add(e);
        }
        it = gheFactory.iterator();
        while(it.hasNext()) {
            Event e = it.next();
            addEvent(e);
            if(e instanceof Restart)
                ((Restart)e).setEventList(
                        restartableEvents.toArray(new Event[0]));
        }
    }

}

